package parser;

import lexer.Lexer;
import lexer.Token;

import java.util.ArrayList;

/**
 * @author IDo!
 * @version 1.0
 */
public class Parser {
    private Lexer _lexer;

    public Parser(Lexer lexer) {
        this._lexer = lexer;
    }

    /**
     *  从文法起始term开始
     *
     *  Term::=LAMBDA LCID DOT Term
     *         | Application
     *  Application::=Atom Application
     *         | Empty
     *  Atom::=LPAREN Term RPAREN
     *         | LCID
     */
    public AST _Set() {
        ArrayList<String> ctx = new ArrayList<String>();
        AST ret = this.term(ctx);
        this._lexer.match(Token.Type.EOF);
//        System.out.println(ret);
        return ret;
    }

    /**
     *   通过递归获取Term项（abstraction结点）
     *
     *   Term::=LAMBDA LCID DOT Term
     *          | Application
     **/
    public AST term(ArrayList<String> ctx) {
        if (this._lexer.skip(Token.Type.LAMBDA)) {
            String id = this._lexer.token(Token.Type.LCID).toString();
            ArrayList<String> temp = new ArrayList<>();
            this._lexer.match(Token.Type.DOT);
            temp.add(id);
            temp.addAll(ctx);
            AST term = this.term(temp);

            //递归获取下一个term项
            return new AST(new StringBuilder(id), term);
        } else {
            return this.application(ctx);
        }
    }

    /**
     * 删除文法左递归
     *    Application::=Application Atom | Atom
     * -> Application ::=Atom ... Atom
     * -> Application'::=Atom Application'
     *                  | 'Empty
     *
     * 获取应用（application）结点
     **/
    public AST application(ArrayList<String> ctx) {
        AST left = this.atom(ctx);
        while (true) {
            AST right = this.atom(ctx);
            if (right == null) return left;       //check whether the recursion overs and return left part
            else left = new AST(left, right);  //if not, build left&right subtrees
        }
    }

    /**
     * 获取原子（identifier结点）
     *
     *   Atom::=LPAREN Term RPAREN
     *         | LCID
     **/
    public AST atom(ArrayList<String> ctx) {
        if (this._lexer.skip(Token.Type.LPAREN)) {
            AST term = this.term(ctx);
            this._lexer.match(Token.Type.RPAREN);
            return term;
        }                                     //check LPAREN and return Term
        else if (this._lexer.check(Token.Type.LCID)) {
            StringBuilder id = this._lexer.token(Token.Type.LCID);
            if (ctx.indexOf(id.toString()) == -1)
                return new AST(-1, id.toString()); //free variable
            return new AST(ctx.indexOf(id.toString()));
        }                                    //check LCID
        else return null;
    }
}
